using System;
using System.Linq;
using Xunit;
using Xunit.Abstractions;
using FrameIdentityReq.Frames;
using FrameIdentityReq.Network;

namespace xUnitTest
{
    public class TestRestoreFrame
    {
        private readonly ITestOutputHelper Output;

        public TestRestoreFrame(ITestOutputHelper output)
        {
            Output = output;
        }

        class EventLog
        {
            public string UnknowFrameError = "";
            public string ProtocolVersionError = "";
            public string FactoryCodeError = "";
            public string FrameTypeError = "";
            public string RestoredBaseFrame = "";
            public string RestoredIdentityReqFrame = "";
            public string RestoreddIdentityAckFrame = "";

            public EventHandler<SocketState> NotUnknowFrameError;
            public EventHandler<SocketState> NotProtocolVersionError;
            public EventHandler<SocketState> NotFactoryCodeError;
            public EventHandler<SocketState> NotFrameTypeError;
            public EventHandler<SocketState> NotRestoredBaseFrame;
            public EventHandler<SocketState> NotRestoredIdentityReqFrame;
            public EventHandler<SocketState> NotRestoredIdentityAckFrame;

            public EventLog()
            {
                NotUnknowFrameError = (o, s) => UnknowFrameError = "不应触发事件: UnknowFrameError";
                NotProtocolVersionError = (o, s) => ProtocolVersionError = "不应触发事件: ProtocolVersionError";
                NotFactoryCodeError = (o, s) => FactoryCodeError = "不应触发事件: FactoryCodeError";
                NotFrameTypeError = (o, s) => FrameTypeError = "不应触发事件: FrameTypeError";
                NotRestoredBaseFrame = (o, s) => RestoredBaseFrame = "不应触发事件: RestoredBaseFrame";
                NotRestoredIdentityReqFrame = (o, s) => RestoredIdentityReqFrame = "不应触发事件: RestoredIdentityReqFrame";
                NotRestoredIdentityAckFrame = (o, s) => RestoreddIdentityAckFrame = "不应触发事件: RestoreddIdentityAckFrame";
            }

            public void Attach()
            {
                BaseFrame.OnUnknowFrameError += NotUnknowFrameError;
                BaseFrame.OnProtocolVersionError += NotProtocolVersionError;
                BaseFrame.OnFactoryCodeError += NotFactoryCodeError;
                BaseFrame.OnFrameTypeError += NotFrameTypeError;
                BaseFrame.OnRestored += NotRestoredBaseFrame;
                IdentityReqFrame.OnRestored += NotRestoredIdentityReqFrame;
                IdentityAckFrame.OnRestored += NotRestoredIdentityAckFrame;
            }

            public void Detach()
            {
                BaseFrame.OnUnknowFrameError -= NotUnknowFrameError;
                BaseFrame.OnProtocolVersionError -= NotProtocolVersionError;
                BaseFrame.OnFactoryCodeError -= NotFactoryCodeError;
                BaseFrame.OnFrameTypeError -= NotFrameTypeError;
                BaseFrame.OnRestored -= NotRestoredBaseFrame;
                IdentityReqFrame.OnRestored -= NotRestoredIdentityReqFrame;
                IdentityAckFrame.OnRestored -= NotRestoredIdentityAckFrame;
            }

            public void Checking()
            {
                Assert.Equal("", UnknowFrameError);
                Assert.Equal("", ProtocolVersionError);
                Assert.Equal("", FactoryCodeError);
                Assert.Equal("", FrameTypeError);
                Assert.Equal("", RestoredBaseFrame);
                Assert.Equal("", RestoredIdentityReqFrame);
                Assert.Equal("", RestoreddIdentityAckFrame);
            }
        }

        [Fact]
        public void TestRestoreBaseFrame()
        {
            // Give
            EventLog eventLog = new EventLog();
            BaseFrame frame = new BaseFrame();
            frame.FactoryCode.Value = 4;
            frame.CRC.Value = frame.CalculationCRC();
            FrameIdentityReq.Network.SocketState state = new FrameIdentityReq.Network.SocketState();
            state.Buffer = frame.Buffer.ToArray();

            // Then
            Assert.RaisedEvent<SocketState> re = Assert.Raises<SocketState>(
                 (EventHandler<SocketState> h) =>
                 {
                     eventLog.Attach();
                     BaseFrame.OnRestored -= eventLog.NotRestoredBaseFrame;
                     BaseFrame.OnRestored += h;
                 },
                 (EventHandler<SocketState> h) =>
                 {
                     eventLog.Detach();
                     BaseFrame.OnRestored -= h;
                 },
                 () => BaseFrame.Restore(state));

            Assert.IsType<BaseFrame>(re.Sender);
            Assert.IsType<SocketState>(re.Arguments);
            Assert.IsType<FrameIdentityReq.Network.SocketState>(re.Arguments);
            Assert.NotNull(re.Sender);
            Assert.NotNull(re.Arguments);
            Assert.NotNull(re.Arguments);
            Assert.Equal(frame.Buffer, re.Arguments.Buffer);

            eventLog.Checking();
        }


        // 测试异常: 空buffer
        [Fact]
        public void TestRestoreBaseFrameForNullBuffer()
        {

            //Given
            EventLog eventLog = new EventLog();

            //When
            FrameIdentityReq.Network.SocketState state = new FrameIdentityReq.Network.SocketState();

            //Then
            Assert.RaisedEvent<SocketState> re = Assert.Raises<SocketState>(
                 (EventHandler<SocketState> h) =>
                 {
                     eventLog.Attach();
                     BaseFrame.OnUnknowFrameError -= eventLog.NotUnknowFrameError;
                     BaseFrame.OnUnknowFrameError += h;
                 },
                 (EventHandler<SocketState> h) =>
                 {
                     eventLog.Detach();
                     BaseFrame.OnUnknowFrameError -= h;
                 },
                 () => BaseFrame.Restore(state));

            eventLog.Checking();
        }

        // 测试异常: 短buffer
        [Fact]
        public void TestRestoreBaseFrameForShortBuffer()
        {
            //Given
            EventLog eventLog = new EventLog();
            FrameIdentityReq.Network.SocketState state = new FrameIdentityReq.Network.SocketState();
            BaseFrame frame = new BaseFrame();

            //When
            byte[] buffer = new byte[frame.Buffer.Count - 1];
            state.Buffer = buffer;

            //Then
            Assert.RaisedEvent<SocketState> re = Assert.Raises<SocketState>(
                 (EventHandler<SocketState> h) =>
                 {
                     eventLog.Attach();
                     BaseFrame.OnUnknowFrameError -= eventLog.NotUnknowFrameError;
                     BaseFrame.OnUnknowFrameError += h;
                 },
                 (EventHandler<SocketState> h) =>
                 {
                     eventLog.Detach();
                     BaseFrame.OnUnknowFrameError -= h;
                 },
                 () => BaseFrame.Restore(state));

            eventLog.Checking();
        }


        // 测试异常: 帧类型
        [Theory]
        [InlineData(21)]
        public void TestRestoreBaseFrameForFrameTypeError(byte frameType)
        {
            //Given
            EventLog eventLog = new EventLog();
            BaseFrame frame = new BaseFrame();
            frame.FactoryCode.Value = 4;

            //When
            frame.FrameType.Value = (FrameTypeEnum)frameType;
            frame.CRC.Value = frame.CalculationCRC();
            FrameIdentityReq.Network.SocketState state = new FrameIdentityReq.Network.SocketState();
            state.Buffer = frame.Buffer.ToArray();

            //Then
            Assert.RaisedEvent<SocketState> re = Assert.Raises<SocketState>(
                 (EventHandler<SocketState> h) =>
                 {
                     eventLog.Attach();
                     BaseFrame.OnFrameTypeError -= eventLog.NotFrameTypeError;
                     BaseFrame.OnFrameTypeError += h;
                 },
                 (EventHandler<SocketState> h) =>
                 {
                     eventLog.Detach();
                     BaseFrame.OnFrameTypeError -= h;
                 },
                 () => BaseFrame.Restore(state));

            eventLog.Checking();
        }

        // 测试异常：Crc校验错误
        [Theory]
        [InlineData(0)]
        [InlineData(0xFFFF)]
        public void TestRestoreBaseFrameForCrcError(UInt16 crc)
        {
            //Given
            EventLog eventLog = new EventLog();
            BaseFrame frame = new BaseFrame();
            frame.FactoryCode.Value = 4;

            //When
            frame.CRC.Value = crc;
            FrameIdentityReq.Network.SocketState state = new FrameIdentityReq.Network.SocketState();
            state.Buffer = frame.Buffer.ToArray();

            //Then
            Assert.RaisedEvent<SocketState> re = Assert.Raises<SocketState>(
                 (EventHandler<SocketState> h) =>
                 {
                     eventLog.Attach();
                     BaseFrame.OnUnknowFrameError -= eventLog.NotUnknowFrameError;
                     BaseFrame.OnUnknowFrameError += h;
                 },
                 (EventHandler<SocketState> h) =>
                 {
                     eventLog.Detach();
                     BaseFrame.OnUnknowFrameError -= h;
                 },
                 () => BaseFrame.Restore(state));

            eventLog.Checking();
        }

        // 测试异常：协议版本错误
        [Theory]
        [InlineData(0)]
        [InlineData(3)]
        [InlineData(5)]
        [InlineData(15)]
        public void TestRestoreBaseFrameForProtoclVersionError(int protocolVersion)
        {
            //Given
            EventLog eventLog = new EventLog();
            BaseFrame frame = new BaseFrame();
            frame.FactoryCode.Value = 4;

            //When
            frame.ProtocolVersion.Value = protocolVersion;
            frame.CRC.Value = frame.CalculationCRC();
            FrameIdentityReq.Network.SocketState state = new FrameIdentityReq.Network.SocketState();
            state.Buffer = frame.Buffer.ToArray();

            //Then
            Assert.RaisedEvent<SocketState> re = Assert.Raises<SocketState>(
                 (EventHandler<SocketState> h) =>
                 {
                     eventLog.Attach();
                     BaseFrame.OnProtocolVersionError -= eventLog.NotProtocolVersionError;
                     BaseFrame.OnProtocolVersionError += h;
                 },
                 (EventHandler<SocketState> h) =>
                 {
                     eventLog.Detach();
                     BaseFrame.OnProtocolVersionError -= h;
                 },
                 () => BaseFrame.Restore(state));

            eventLog.Checking();
        }

        // 测试异常：工厂号错误
        [Theory]
        [InlineData(0)]
        [InlineData(3)]
        [InlineData(5)]
        [InlineData(15)]
        public void TestRestoreBaseFrameForFactoryCodeError(int factoryCode)
        {
            //Given
            EventLog eventLog = new EventLog();
            BaseFrame frame = new BaseFrame();
            frame.FactoryCode.Value = 4;

            //When
            frame.FactoryCode.Value = factoryCode;
            frame.CRC.Value = frame.CalculationCRC();
            FrameIdentityReq.Network.SocketState state = new FrameIdentityReq.Network.SocketState();
            state.Buffer = frame.Buffer.ToArray();

            //Then
            Assert.RaisedEvent<SocketState> re = Assert.Raises<SocketState>(
                 (EventHandler<SocketState> h) =>
                 {
                     eventLog.Attach();
                     BaseFrame.OnFactoryCodeError -= eventLog.NotFactoryCodeError;
                     BaseFrame.OnFactoryCodeError += h;
                 },
                 (EventHandler<SocketState> h) =>
                 {
                     eventLog.Detach();
                     BaseFrame.OnFactoryCodeError -= h;
                 },
                 () => BaseFrame.Restore(state));

            eventLog.Checking();
        }

        [Fact]
        public void TestRestoreIdentityReqFrame()
        {
            EventLog eventLog = new EventLog();
            IdentityReqFrame frame = new IdentityReqFrame();
            frame.FactoryCode.Value = 4;
            frame.HardwareSerialNum.Value = "000000000000223";
            frame.UserName.Value = "ljt";
            frame.PWD.Value = "abc123";
            frame.CRC.Value = frame.CalculationCRC();
            FrameIdentityReq.Network.SocketState state = new FrameIdentityReq.Network.SocketState();
            state.Buffer = frame.Buffer.ToArray();

            Assert.RaisedEvent<SocketState> re = Assert.Raises<SocketState>(
                 (EventHandler<SocketState> h) =>
                 {
                     eventLog.Attach();
                     IdentityReqFrame.OnRestored -= eventLog.NotRestoredIdentityReqFrame;
                     IdentityReqFrame.OnRestored += h;
                 },
                 (EventHandler<SocketState> h) =>
                 {
                     eventLog.Detach();
                     IdentityReqFrame.OnRestored -= h;
                 },
                 () => BaseFrame.Restore(state));

            Assert.True(typeof(IdentityReqFrame) == re.Sender.GetType());
            Assert.IsType<SocketState>(re.Arguments);
            Assert.IsType<FrameIdentityReq.Network.SocketState>(re.Arguments);
            Assert.NotNull(re.Sender);
            Assert.NotNull(re.Arguments);
            Assert.Equal(frame.Buffer, re.Arguments.Buffer);

            eventLog.Checking();
        }

        // 测试异常: 短buffer
        [Fact]
        public void TestRestoreIdentityReqFrameForShortBuffer()
        {
            //Given
            EventLog eventLog = new EventLog();
            FrameIdentityReq.Network.SocketState state = new FrameIdentityReq.Network.SocketState();
            IdentityReqFrame frame = new IdentityReqFrame();
            frame.HardwareSerialNum.Value = "000000000000223";
            frame.UserName.Value = "ljt";
            frame.PWD.Value = "abc123";

            //When
            byte[] buffer = new byte[frame.Buffer.Count - 1];
            state.Buffer = buffer;

            //Then
            Assert.RaisedEvent<SocketState> re = Assert.Raises<SocketState>(
                 (EventHandler<SocketState> h) =>
                 {
                     eventLog.Attach();
                     BaseFrame.OnUnknowFrameError -= eventLog.NotUnknowFrameError;
                     BaseFrame.OnUnknowFrameError += h;
                 },
                 (EventHandler<SocketState> h) =>
                 {
                     eventLog.Detach();
                     BaseFrame.OnUnknowFrameError -= h;
                 },
                 () => BaseFrame.Restore(state));

            eventLog.Checking();
        }

        // 测试异常: 帧类型
        [Theory]
        [InlineData(21)]
        public void TestRestoreIdentityReqFrameForFrameTypeError(byte frameType)
        {
            //Given
            EventLog eventLog = new EventLog();
            IdentityReqFrame frame = new IdentityReqFrame();
            frame.FactoryCode.Value = 4;
            frame.HardwareSerialNum.Value = "000000000000223";
            frame.UserName.Value = "ljt";
            frame.PWD.Value = "abc123";

            //When
            frame.FrameType.Value = (FrameTypeEnum)frameType;
            frame.CRC.Value = frame.CalculationCRC();
            FrameIdentityReq.Network.SocketState state = new FrameIdentityReq.Network.SocketState();
            state.Buffer = frame.Buffer.ToArray();

            //Then
            Assert.RaisedEvent<SocketState> re = Assert.Raises<SocketState>(
                 (EventHandler<SocketState> h) =>
                 {
                     eventLog.Attach();
                     BaseFrame.OnFrameTypeError -= eventLog.NotFrameTypeError;
                     BaseFrame.OnFrameTypeError += h;
                 },
                 (EventHandler<SocketState> h) =>
                 {
                     eventLog.Detach();
                     BaseFrame.OnFrameTypeError -= h;
                 },
                 () => BaseFrame.Restore(state));

            eventLog.Checking();
        }

        // 测试异常：Crc校验错误
        [Theory]
        [InlineData(0)]
        [InlineData(0xFFFF)]
        public void TestRestoreIdentityReqFrameForCrcError(UInt16 crc)
        {
            //Given
            EventLog eventLog = new EventLog();
            IdentityReqFrame frame = new IdentityReqFrame();
            frame.FactoryCode.Value = 4;
            frame.HardwareSerialNum.Value = "000000000000223";
            frame.UserName.Value = "ljt";
            frame.PWD.Value = "abc123";

            //When
            frame.CRC.Value = crc;
            FrameIdentityReq.Network.SocketState state = new FrameIdentityReq.Network.SocketState();
            state.Buffer = frame.Buffer.ToArray();

            //Then
            Assert.RaisedEvent<SocketState> re = Assert.Raises<SocketState>(
                 (EventHandler<SocketState> h) =>
                 {
                     eventLog.Attach();
                     BaseFrame.OnUnknowFrameError -= eventLog.NotUnknowFrameError;
                     BaseFrame.OnUnknowFrameError += h;
                 },
                 (EventHandler<SocketState> h) =>
                 {
                     eventLog.Detach();
                     BaseFrame.OnUnknowFrameError -= h;
                 },
                 () => BaseFrame.Restore(state));

            eventLog.Checking();
        }

        // 测试异常：协议版本错误
        [Theory]
        [InlineData(0)]
        [InlineData(3)]
        [InlineData(5)]
        [InlineData(15)]
        public void TestRestoreIdentityReqFrameForProtoclVersionError(int protocolVersion)
        {
            //Given
            EventLog eventLog = new EventLog();
            IdentityReqFrame frame = new IdentityReqFrame();
            frame.FactoryCode.Value = 4;
            frame.HardwareSerialNum.Value = "000000000000223";
            frame.UserName.Value = "ljt";
            frame.PWD.Value = "abc123";

            //When
            frame.ProtocolVersion.Value = protocolVersion;
            frame.CRC.Value = frame.CalculationCRC();
            FrameIdentityReq.Network.SocketState state = new FrameIdentityReq.Network.SocketState();
            state.Buffer = frame.Buffer.ToArray();

            //Then
            Assert.RaisedEvent<SocketState> re = Assert.Raises<SocketState>(
                 (EventHandler<SocketState> h) =>
                 {
                     eventLog.Attach();
                     BaseFrame.OnProtocolVersionError -= eventLog.NotProtocolVersionError;
                     BaseFrame.OnProtocolVersionError += h;
                 },
                 (EventHandler<SocketState> h) =>
                 {
                     eventLog.Detach();
                     BaseFrame.OnProtocolVersionError -= h;
                 },
                 () => BaseFrame.Restore(state));

            eventLog.Checking();
        }

        // 测试异常：工厂号错误
        [Theory]
        [InlineData(0)]
        [InlineData(3)]
        [InlineData(5)]
        [InlineData(15)]
        public void TestRestoreIdentityReqFrameForFactoryCodeError(int factoryCode)
        {
            //Given
            EventLog eventLog = new EventLog();
            IdentityReqFrame frame = new IdentityReqFrame();
            frame.FactoryCode.Value = 4;
            frame.HardwareSerialNum.Value = "000000000000223";
            frame.UserName.Value = "ljt";
            frame.PWD.Value = "abc123";

            //When
            frame.FactoryCode.Value = factoryCode;
            frame.CRC.Value = frame.CalculationCRC();
            FrameIdentityReq.Network.SocketState state = new FrameIdentityReq.Network.SocketState();
            state.Buffer = frame.Buffer.ToArray();

            //Then
            Assert.RaisedEvent<SocketState> re = Assert.Raises<SocketState>(
                 (EventHandler<SocketState> h) =>
                 {
                     eventLog.Attach();
                     BaseFrame.OnFactoryCodeError -= eventLog.NotFactoryCodeError;
                     BaseFrame.OnFactoryCodeError += h;
                 },
                 (EventHandler<SocketState> h) =>
                 {
                     eventLog.Detach();
                     BaseFrame.OnFactoryCodeError -= h;
                 },
                 () => BaseFrame.Restore(state));

            eventLog.Checking();
        }

        [Fact]
        public void TestRestoreIdentityAckFrame()
        {
            // Give
            EventLog eventLog = new EventLog();
            IdentityAckFrame frame = new IdentityAckFrame();
            frame.FactoryCode.Value = 4;
            frame.HardwareSerialNum.Value = "000000000000223";
            frame.ValidationCode.Value = ValidationCodeEnum.success;
            frame.DeviceCommunicateID.Value = new byte[] { 88, 77, 66 };
            frame.ServerDateTime.Value = DateTime.Now;
            frame.CRC.Value = frame.CalculationCRC();
            FrameIdentityReq.Network.SocketState state = new FrameIdentityReq.Network.SocketState();
            state.Buffer = frame.Buffer.ToArray();

            // Then
            Assert.RaisedEvent<SocketState> re = Assert.Raises<SocketState>(
                 (EventHandler<SocketState> h) =>
                 {
                     eventLog.Attach();
                     IdentityAckFrame.OnRestored -= eventLog.NotRestoredIdentityAckFrame;
                     IdentityAckFrame.OnRestored += h;
                 },
                 (EventHandler<SocketState> h) =>
                 {
                     eventLog.Detach();
                     IdentityAckFrame.OnRestored -= h;
                 },
                 () => BaseFrame.Restore(state));

            // Assert.False(typeof(IdentityReqFrame) == re.Sender.GetType());
            Assert.True(typeof(IdentityAckFrame) == re.Sender.GetType());
            Assert.IsType<SocketState>(re.Arguments);
            Assert.IsType<FrameIdentityReq.Network.SocketState>(re.Arguments);
            Assert.NotNull(re.Sender);
            Assert.NotNull(re.Arguments);
            Assert.Equal(frame.Buffer, re.Arguments.Buffer);

            eventLog.Checking();
        }
    }
}